/* eslint-disable @typescript-eslint/no-explicit-any */
import { dom, Nodes } from '@knno/dom';
import { ColumnOption, getText, getValue, List, list, SelectType } from '../list/list';
import { input } from '../input/input';
import SVGSearch from '@material-design-icons/svg/filled/search.svg';
import { popListStyle } from './popList.css';
import { AdaptivePopup } from '../adaptivePopup/adaptivePopup';
import { PopupPosition, Pos } from '../popup/popup';
import '../../dict';
import { GridConfig } from '../grid/grid';

function getRowText(row: RowType, col: ColumnOption) {
	const valueKey = col.text ?? col.value;
	if (valueKey === undefined || row === undefined || row === null || typeof row !== 'object') {
		return row;
	} else {
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		return (row as any)[valueKey];
	}
}

function getRowChildren(row: RowType, col: ColumnOption): RowType[] | null {
	const childrenKey = col.children;
	if (childrenKey === undefined || row === undefined || row === null || typeof row !== 'object') {
		return null;
	} else {
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		return (row as any)[childrenKey];
	}
}

export type RowType = Record<string | number, unknown> | unknown;

function filterData(data: RowType[], opt: ColumnOption, keyword: string): RowType[] {
	const result: RowType[] = [];
	function iterate(rows: RowType[]) {
		rows.forEach((row) => {
			const children = getRowChildren(row, opt);
			const t = getRowText(row, opt);
			if (t !== undefined && t !== null) {
				if ((t + '').includes(keyword)) {
					result.push(row);
				}
			}
			children && iterate(children);
		});
	}
	iterate(data);
	return result;
}

export type OpenSelectOptions = {
	refer?: Nodes;
	multiSelect?: boolean;
	multiSelectType?: SelectType;
	selected?: unknown[];
	activeValue?: unknown;
	useAction?: boolean;
	useSearch?: boolean;
	maxShowLines?: number;
	focus?: boolean;
	popupPosition?: PopupPosition | Pos;
	width?: number | string;
	disableField?: number | string | null;
	noSelectField?: number | string | null;
	resultTextField?: number | string | null;
	/**
	 * Whether to return the full path of the selection, which can be a boolean or a string. If it is a string, it is considered that the full path is returned, and the specified string is used as the path separator.
	 */
	resultFullPath?: boolean | string;
	gridConfig?: Partial<GridConfig>;
	highlighHover?: boolean;
};

export interface SelectHandler {
	list: List;
	close(): void;
	onSelect(callback: SelectCallback): this;
	onSelectMultiple(callback: SelectMultipleCallback): this;
	onClose(callback: () => void): this;
}

function getFullPath(data: RowType[], row: RowType, col: ColumnOption, splitter: string): string | null {
	for (const v of data) {
		if (v === row) {
			return getRowText(v, col);
		} else {
			const children = getRowChildren(v, col);
			if (children) {
				const childText = getFullPath(children, row, col, splitter);
				if (childText != null) {
					return getRowText(v, col) + splitter + childText;
				}
			}
		}
	}
	return null;
}

export type SelectCallback = (value: unknown, text: unknown, row: number) => void;
export type SelectMultipleCallback = (selected: { value: unknown; text: unknown }[]) => void;

export class PopList {
	static show(
		data: RowType[],
		column: ColumnOption = {},
		{
			refer,
			activeValue,
			multiSelect = false,
			multiSelectType = 'any',
			selected,
			useAction = false,
			useSearch = false,
			focus = false,
			maxShowLines = 8,
			popupPosition = 'bottom-left',
			gridConfig,
			width,
			disableField,
			noSelectField,
			resultTextField,
			resultFullPath,
			highlighHover = true,
		}: OpenSelectOptions = {}
	): SelectHandler {
		let onClose: (() => void) | undefined;
		let onSelect: SelectCallback | undefined;
		let onSelectMultiple: SelectMultipleCallback | undefined;
		const selectRow = () => {
			const activeRow = l.active();
			if (activeRow !== null) {
				if (resultTextField !== undefined && resultTextField !== null) {
					const row = l.activeRow() as any;
					onSelect?.(l.activeValue(), row?.[resultTextField], activeRow);
				} else if (resultFullPath) {
					const row = l.activeRow() as any;
					const fullPath = getFullPath(data, row, column, typeof resultFullPath === 'string' ? resultFullPath : ' / ');
					onSelect?.(l.activeValue(), fullPath, activeRow);
				} else {
					onSelect?.(l.activeValue(), l.activeText(), activeRow);
				}
			}
			pop.close();
			setTimeout(() => {
				refer?.focus();
			});
		};
		const l = list(...(highlighHover ? ['highligh-hover'] : []))
			.column({ ...column, nonSelectableField: noSelectField ?? undefined })
			.rows(data)
			.activable(!multiSelect)
			.selectable(multiSelect)
			.selectType(multiSelectType)
			.showEmpty(false)
			.autoHeight(true)
			.disableOn(disableField ?? null)
			.css({
				border: 'none',
				borderRadius: '0',
				// width: this.innerWidth() + 'px',
			})
			.on('rowclick', (event) => {
				if (multiSelect) return;
				if (
					noSelectField !== undefined &&
					noSelectField !== null &&
					!!(event.detail.row as any)?.row?.[noSelectField]
				) {
					return;
				}
				selectRow();
			})
			.on('rowpress', (event) => {
				if (multiSelect) return;
				event.preventDefault();
				event.stopPropagation();
				event.stopImmediatePropagation();
				if (event.detail.key === 'Enter') {
					if (
						noSelectField !== undefined &&
						noSelectField !== null &&
						!!(event.detail.row as any)?.row?.[noSelectField]
					) {
						return;
					}
					selectRow();
				}
			});
		if (multiSelect && selected) {
			l.selected(selected);
		}
		if (gridConfig) {
			l.config(gridConfig);
		}

		function createSearch() {
			const ipt = input()
				.prefixIcon(SVGSearch)
				.clearable(true)
				.on('keydown', (e) => {
					if (e.key === 'ArrowDown') {
						l.focus();
						if (l.active() === null) {
							l.active(0);
						}
						e.stopPropagation();
						e.preventDefault();
					}
				})
				.on('clear', () => {
					l.column(column);
					l.rows(data);
				})
				.on('input', () => {
					const keyword = (ipt.value() + '').trim();
					if (keyword) {
						const newOpt = { ...column };
						newOpt.children = undefined;
						l.column(newOpt);
						l.rows(filterData(data, column, keyword));
					} else {
						l.column(column);
						l.rows(data);
					}
				});
			return ipt;
		}

		const container = dom.div(popListStyle).append(...(useSearch ? [createSearch()] : []), l);
		const pop = AdaptivePopup.show(container, {
			refer,
			popupPosition,
			focus: false,
			useAction,
			width,
			noActionBar: !multiSelect,
		})
			.onOpen(() => {
				l.css('maxHeight', l.getLineHeight() * maxShowLines + 'px').refresh();
				if (activeValue !== undefined) {
					l.activeValue(activeValue, true);
				}
				if (focus) l.focus();
			})
			.onClose(() => {
				onClose?.();
			})
			.onOK(() => {
				// process multiple selection
				onSelectMultiple?.(
					l.selected().map((row: any) => {
						if (resultTextField !== undefined && resultTextField !== null) {
							return { value: getValue(row, column), text: row?.[resultTextField] };
						} else if (resultFullPath) {
							const fullPath = getFullPath(
								data,
								row,
								column,
								typeof resultFullPath === 'string' ? resultFullPath : ' / '
							);
							return { value: getValue(row, column), text: fullPath };
						} else {
							return { value: getValue(row, column), text: getText(row, column) };
						}
					})
				);
				pop.close();
				setTimeout(() => {
					refer?.focus();
				});
			});
		return {
			list: l,
			close() {
				pop.close();
			},
			onSelect(callback) {
				onSelect = callback;
				return this;
			},
			onSelectMultiple(multipleCallback) {
				onSelectMultiple = multipleCallback;
				return this;
			},
			onClose(callback) {
				onClose = callback;
				return this;
			},
		};
	}
}
